package main

//结构体是将零个或多个任意类型的变量，组合在一起的聚合数据类型，也可以看做是数据的集合。
import (
	"encoding/json"
	"fmt"
)

type Person struct {
	name      string
	age       int
	isMarried bool
}

type Student struct {
	sid    int
	name   string
	course []string           //  报名课程
	score  map[string]float64 // 各科成绩
}
type user struct {
	name string
	age  int
}
type Result struct {
	Code    int
	Message string
}

func CourseInit(stu *Student) {
	//在Go语言中，访问结构体指针的成员变量时可以继续使用.，这是因为Go语言为了方便开发者访问结构体指针的成员变量可以像访问结构体的成员变量一样简单，使用了语法糖（Syntactic sugar）技术，将 instance.Name 形式转换为 (*instance).Name。
	//(*stu).course = make([]string, 5) //相同表示
	stu.course = make([]string, 5) // 相同表示
}

// 结构体user Read方法
func (u *user) Read() string {
	return fmt.Sprintf("%s 已经 %d 岁了", u.name, u.age)
}
func (u *user) Set(name string, age int) {
	u.name = name
	u.age = age
}
func (u *user) newSet(name string, age int) {
	u.name = name
	u.age = age
}
func NewStudent(sid int, name string, sex byte, age int, addr string) *Student {

	return &Student{
		sid:  sid,
		name: name,
	}
}
func main() {

	// 初始化
	u := user{
		name: "kobe",
		age:  41,
	}
	uu := user{
		name: "",
		age:  0,
	}
	u.Set("dajiang", 32)
	fmt.Println(u.name, "只有", u.age, "岁")
	// 调用结构体user的 Read方法
	fmt.Println(u.Read())

	uu.newSet("tuantuan", 4)
	fmt.Println(uu.name, "只有", uu.age, "岁")
	// 调用结构体user的 Read方法
	fmt.Println(uu.Read())

	var res Result
	res.Code = 200
	res.Message = "success"

	//序列化
	jsons, errs := json.Marshal(res)
	if errs != nil {
		fmt.Println("json marshal error:", errs)
	}
	fmt.Println("json data :", string(jsons))
	var res2 Result
	errs = json.Unmarshal(jsons, &res2)
	if errs != nil {
		fmt.Println("json unmarshal error:", errs)
	}
	fmt.Println("res2:", res2)

	//匿名结构体
	uu3 := struct {
		name string
		age  int64
	}{name: "dajiang", age: 32}

	fmt.Println(uu3)

	//之前我们学习过值类型和引用类型，知道值类型是变量对应的地址直接存储值，而引用类型是变量对应地址存储的是地址。因为结构体因为是值类型，所以p的地址与存储的第一个值的地址是相同的，而后面每一个成员变量的地址是连续的。

	var p Person
	p.name = "yuan"
	p.age = 23
	p.isMarried = false
	fmt.Printf("%p\n", &p)
	fmt.Printf("%p\n", &(p.name))
	fmt.Printf("%p\n", &(p.age))
	fmt.Printf("%p\n", &(p.isMarried))

	//实例化之 结构体{}
	// (1) 方式1
	/*var s2 = Student{}
	s2.name = "alvin"
	// (2) 方式2：键值对赋值
	s2 = Student{sid: 1001, name: "alvin", course: []string{"chinese", "math"}}
	fmt.Println(s2.name)
	fmt.Println(s2.score) // 默认值
	// (3) 方式3：多值赋值，多值初始化方式必须初始化结构体的所有字段且每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致。
	s2 = Student{1001, "alvin", []string{"chinese", "math"}, nil}
	fmt.Println(s2.name)
	fmt.Println(s2.course[1])*/

	// 案例1
	s2 := Student{sid: 1001, name: "alvin", course: []string{"chinese", "math"}}
	s3 := s2
	fmt.Println(s3)
	s2.name = "rain"
	fmt.Println(s2.name)
	fmt.Println(s3.name)
	// 如果希望s3的值跟随s2保持一致怎么实现
	s4 := &s2 // var s4 *Student = &s2
	s2.name = "rain"
	fmt.Println(s4.name)

	// 案例2
	//var s = Student{sid: 1001, name: "alvin", course: []string{"chinese", "math"}}
	//CourseInit(s)
	//fmt.Println("s报的课程：",s.course)

	var s = &Student{sid: 1001, name: "alvin", course: []string{"chinese", "math"}}
	CourseInit(s)
	fmt.Println("s报的课程：", (*s).course) // *s.course的写法是错误的
	fmt.Println("s报的课程：", s.course)

	//9.4、模拟构造函数

	stu := NewStudent(1001, "yuan", 'm', 23, "beijing")
	fmt.Println(stu.sid)
	fmt.Println(stu.name)

}
